Reverse Cryptography

0x00:Introduction

本片文章主要逆向一些CTF中的常见算法,对算法的原理和实现结合进行分析,总结一些常用的方法以供参考

0x01:Base series

0x01:Base64

介绍

Base64可以将ASCII字符串或者是二进制编码成只包含A—Z,a—z,0—9,+,/ 这64个字符( 26个大写字母,26个小写字母,10个数字,1个+,一个 / 刚好64个字符)。这64个字符用6个bit位就可以全部表示出来,一个字节有8个bit 位,那么还剩下两个bit位,这两个bit位用0来补充。其实,一个Base64字符仍然是8个bit位,但是有效部分只有右边的6个 bit,左边两个永远是0。Base64的编码规则是将3个8位字节(3×8=24位)编码成4个6位的字节(4×6=24位),之后在每个6位字节前面,补充两个0,形成4个8位字节的形式,那么取值范围就变成了0~63。又因为2的6次方等于64,所以每6个位组成一个单元。一般在CTF逆向题目中base64的加密过程主要是用自定义的索引表,所以如果能一眼能看出是base64加密就会节约很多时间。

加密过程

  • base64的编码都是按字符串长度,以每3个8bit的字符为一组,
  • 然后针对每组,首先获取每个字符的ASCII编码,
  • 然后将ASCII编码转换成8bit的二进制,得到一组3*8=24bit的字节
  • 然后再将这24bit划分为4个6bit的字节,并在每个6bit的字节前面都填两个高位0,得到4个8bit的字节
  • 然后将这4个8bit的字节转换成10进制,对照Base64编码表 ,得到对应编码后的字符。

索引表如下

索引 对应字符 索引 对应字符 索引 对应字符 索引 对应字符
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w
15 P 32 g 49 x
16 Q 33 h 50 y

例子

第一个例子以base64加密SLF为例子,过程如下

1
2
3
4
5
6
7
8
9
10
字符串      S       L        F
ASCII 83 80 76
二进制 01010011‬ 01001100 01000110
合并 01010011‬0100110001000110
6位 010100 110100 110001 000110
补零 00010100 00110100 00110001 00000110
进制 20 52 49 6
对照 U 0 x G

SLF -> U0xG

第二个例子以base64加密M为例子,过程如下

1
2
3
4
5
6
7
8
9
10
字符串      M
ASCII 77
二进进 01001101
合并 01001101
6位 010011 01
补零 00010011 00010000
进制 19 16
对照 T Q = =

M -> TQ==

实现

最上面的base64char索引表可以自定义,这里用c实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <stdio.h>
#include <string.h>

// 全局常量定义
const char * base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const char padding_char = '=';

/*编码代码
* const unsigned char * sourcedata, 源数组
* char * base64 ,码字保存
*/
int base64_encode(const unsigned char * sourcedata, char * base64)
{
int i = 0, j = 0;
unsigned char trans_index = 0; // 索引是8位,但是高两位都为0
const int datalength = strlen((const char*)sourcedata);
for (; i < datalength; i += 3){
// 每三个一组,进行编码
// 要编码的数字的第一个
trans_index = ((sourcedata[i] >> 2) & 0x3f);
base64[j++] = base64char[(int)trans_index];
// 第二个
trans_index = ((sourcedata[i] << 4) & 0x30);
if (i + 1 < datalength){
trans_index |= ((sourcedata[i + 1] >> 4) & 0x0f);
base64[j++] = base64char[(int)trans_index];
}
else{
base64[j++] = base64char[(int)trans_index];

base64[j++] = padding_char;

base64[j++] = padding_char;

break; // 超出总长度,可以直接break
}
// 第三个
trans_index = ((sourcedata[i + 1] << 2) & 0x3c);
if (i + 2 < datalength){ // 有的话需要编码2个
trans_index |= ((sourcedata[i + 2] >> 6) & 0x03);
base64[j++] = base64char[(int)trans_index];

trans_index = sourcedata[i + 2] & 0x3f;
base64[j++] = base64char[(int)trans_index];
}
else{
base64[j++] = base64char[(int)trans_index];

base64[j++] = padding_char;

break;
}
}

base64[j] = '\0';

return 0;
}

/** 在字符串中查询特定字符位置索引
* const char *str ,字符串
* char c,要查找的字符
*/
int num_strchr(const char *str, char c) //
{
const char *pindex = strchr(str, c);
if (NULL == pindex){
return -1;
}
return pindex - str;
}
/* 解码
* const char * base64 码字
* unsigned char * dedata, 解码恢复的数据
*/
int base64_decode(const char * base64, unsigned char * dedata)
{
int i = 0, j = 0;
int trans[4] = { 0, 0, 0, 0 };
for (; base64[i] != '\0'; i += 4){
// 每四个一组,译码成三个字符
trans[0] = num_strchr(base64char, base64[i]);
trans[1] = num_strchr(base64char, base64[i + 1]);
// 1/3
dedata[j++] = ((trans[0] << 2) & 0xfc) | ((trans[1] >> 4) & 0x03);

if (base64[i + 2] == '='){
continue;
}
else{
trans[2] = num_strchr(base64char, base64[i + 2]);
}
// 2/3
dedata[j++] = ((trans[1] << 4) & 0xf0) | ((trans[2] >> 2) & 0x0f);

if (base64[i + 3] == '='){
continue;
}
else{
trans[3] = num_strchr(base64char, base64[i + 3]);
}

// 3/3
dedata[j++] = ((trans[2] << 6) & 0xc0) | (trans[3] & 0x3f);
}

dedata[j] = '\0';

return 0;
}

// 测试
int main()
{
const unsigned char str[] = "a45rbcd";
const unsigned char *sourcedata = str;
char base64[128];
base64_encode(sourcedata, base64);

printf("编码:%s\n", base64);

char dedata[128];

base64_decode(base64, (unsigned char*)dedata);

printf("译码:%s", dedata);

getchar();
getchar();
return 0;
}

输入如下

1
2
3
4
5
C:\Users\thunder>"D:\AlgorithmTest.exe"
编码:YTQ1cmJjZA==
译码:a45rbcd

C:\Users\thunder>

上面的代码是base64加密和解密字符串a45rbcd我们用IDA查看,base64char即是我们的索引表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
int __cdecl base64_encode(const char *sourcedata, char *base64)
{
int v2; // STEC_4
int v3; // STEC_4
int v4; // STEC_4
signed int datalength; // [esp+D0h] [ebp-2Ch]
unsigned __int8 trans_index; // [esp+DFh] [ebp-1Dh]
unsigned __int8 trans_indexa; // [esp+DFh] [ebp-1Dh]
int j; // [esp+E8h] [ebp-14h]
int ja; // [esp+E8h] [ebp-14h]
int jb; // [esp+E8h] [ebp-14h]
int i; // [esp+F4h] [ebp-8h]

i = 0;
j = 0;
datalength = j__strlen(sourcedata);
while ( i < datalength )
{
base64[j] = base64char[((signed int)(unsigned __int8)sourcedata[i] >> 2) & 0x3F]; # 右移
ja = j + 1;
trans_index = 16 * sourcedata[i] & 0x30;
if ( i + 1 >= datalength )
{
base64[ja] = base64char[trans_index];
v2 = ja + 1;
base64[v2++] = padding_char;
base64[v2] = padding_char;
j = v2 + 1;
break;
}
base64[ja] = base64char[((signed int)(unsigned __int8)sourcedata[i + 1] >> 4) & 0xF | trans_index]; # 右移
jb = ja + 1;
trans_indexa = 4 * sourcedata[i + 1] & 0x3C;
if ( i + 2 >= datalength )
{
base64[jb] = base64char[trans_indexa];
v4 = jb + 1;
base64[v4] = padding_char;
j = v4 + 1;
break;
}
base64[jb] = base64char[((signed int)(unsigned __int8)sourcedata[i + 2] >> 6) & 3 | trans_indexa]; # 右移
v3 = jb + 1;
base64[v3] = base64char[sourcedata[i + 2] & 0x3F];
j = v3 + 1;
i += 3;
}
base64[j] = 0;
return 0;
}

辨别

其实辨别很简单,有很多的方法,最简单的方法就是动态调试,直接用OD或者IDA动态调试,多输入几组数据,观察加密后的字符串,存在=这种字符串多半都有base64加密。 如果不能动态调试那就用IDA静态观察,观察索引表,观察对输入的操作,比如上面很明显的三次右移操作。

解密

一般解密用python来实现

1
2
3
4
5
6
7
import base64
s = 'key' # 要加密的字符串
a = base64.b64encode(s) # 加密

print a

print base64.b64decode(a) # 解密

在线解密网站 : https://www.qqxiuzi.cn/bianma/base.php

CTF参考例题

DDCTF2019 Reverse2

0x02:Base32

原理

Base32编码是使用32个可打印字符(字母A-Z和数字2-7)对任意字节数据进行编码的方案,编码后的字符串不用区分大小写并排除了容易混淆的字符,可以方便地由人类使用并由计算机处理。

符号 符号 符号 符号
0 A 8 I 16 Q 24 Y
1 B 9 J 17 R 25 Z
2 C 10 K 18 S 26 2
3 D 11 L 19 T 27 3
4 E 12 M 20 U 28 4
5 F 13 N 21 V 29 5
6 G 14 O 22 W 30 6
7 H 15 P 23 X 31 7
填充 =

Base32将任意字符串按照字节进行切分,并将每个字节对应的二进制值(不足8比特高位补0)串联起来,按照5比特一组进行切分,并将每组二进制值转换成十进制来对应32个可打印字符中的一个。

由于数据的二进制传输是按照8比特一组进行(即一个字节),因此Base32按5比特切分的二进制数据必须是40比特的倍数(5和8的最小公倍数)。例如输入单字节字符“%”,它对应的二进制值是“100101”,前面补两个0变成“00100101”(二进制值不足8比特的都要在高位加0直到8比特),从左侧开始按照5比特切分成两组:“00100”和“101”,后一组不足5比特,则在末尾填充0直到5比特,变成“00100”和“10100”,这两组二进制数分别转换成十进制数,通过上述表格即可找到其对应的可打印字符“E”和“U”,但是这里只用到两组共10比特,还差30比特达到40比特,按照5比特一组还需6组,则在末尾填充6个“=”。填充“=”符号的作用是方便一些程序的标准化运行,大多数情况下不添加也无关紧要,而且,在URL中使用时必须去掉“=”符号。

与Base64相比,Base32具有许多优点:

  • 适合不区分大小写的文件系统,更利于人类口语交流或记忆。
  • 结果可以用作文件名,因为它不包含路径分隔符 “/”等符号。
  • 排除了视觉上容易混淆的字符,因此可以准确的人工录入。(例如,RFC4648符号集忽略了数字“1”、“8”和“0”,因为它们可能与字母“I”,“B”和“O”混淆)。
  • 排除填充符号“=”的结果可以包含在URL中,而不编码任何字符。

Base32也比Base16有优势:

  • Base32比Base16占用的空间更小。(1000比特数据Base32需要200个字符,而Base16则为250个字符)

Base32的缺点:

  • Base32比Base64多占用大约20%的空间。因为Base32使用8个ASCII字符去编码原数据中的5个字节数据,而Base64是使用4个ASCII字符去编码原数据中的3个字节数据。

解密

1
2
3
4
5
6
7
import base64
s = 'key' # 要加密的字符串
a = base64.b32encode(s) # 加密

print a

print base64.b32decode(a) # 解密

在线网站 : https://www.qqxiuzi.cn/bianma/base.php

CTF参考例题

2017第二届广东省强网杯线上赛 Nonstandard

0x03:Base16

原理

Base16编码使用16个ASCII可打印字符(数字0-9和字母A-F)对任意字节数据进行编码。Base16先获取输入字符串每个字节的二进制值(不足8比特在高位补0),然后将其串联进来,再按照4比特一组进行切分,将每组二进制数分别转换成十进制,在下述表格中找到对应的编码串接起来就是Base16编码。可以看到8比特数据按照4比特切分刚好是两组,所以Base16不可能用到填充符号“=”。

Base16编码后的数据量是原数据的两倍:1000比特数据需要250个字符(即 250*8=2000 比特)。换句话说:Base16使用两个ASCII字符去编码原数据中的一个字节数据。

编码 编码
0 0 8 8
1 1 9 9
2 2 10 A
3 3 11 B
4 4 12 C
5 5 13 D
6 6 14 E
7 7 15 F

Base16编码是一个标准的十六进制字符串(注意是字符串而不是数值),更易被人类和计算机使用,因为它并不包含任何控制字符,以及Base64和Base32中的“=”符号。输入的非ASCII字符,使用UTF-8字符集。

解密

1
2
3
4
5
6
7
import base64
s = 'key' # 要加密的字符串
a = base64.b16encode(s) # 加密

print a

print base64.b16decode(a) # 解密

在线网站 : https://www.qqxiuzi.cn/bianma/base.php

0x02:RC4

原理

在密码学中,RC4(来自Rivest Cipher 4的缩写)是一种流加密算法,密钥长度可变。它加解密使用相同的密钥,因此也属于对称加密算法。RC4是有线等效加密(WEP)中采用的加密算法,也曾经是TLS可采用的算法之一。

加密过程

参数 作用
S-box(S) 256长度的char型数组,定义为: unsigned char sBox[256]
Key(K) 自定义的密钥,用来打乱 S-box
pData 用来加密的数据
  1. 初始化 S (256字节的char型数组),key 是我们自定义的密钥,用来打乱 S ,i 确保 S-box 的每个元素都得到处理, j 保证 S-box 的搅乱是随机的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /*初始化函数*/
    void rc4_init(unsigned char*s, unsigned char*key, unsigned long Len)
    {
    int i = 0, j = 0;
    char k[256] = { 0 };
    unsigned char tmp = 0;
    for (i = 0; i < 256; i++)
    {
    s[i] = i; // 赋值 S
    k[i] = key[i%Len]; // 赋值 K
    }
    for (i = 0; i < 256; i++)
    {
    j = (j + s[i] + k[i]) % 256; // 开始混淆
    tmp = s[i];
    s[i] = s[j]; // 交换s[i]和s[j]
    s[j] = tmp;
    }
    }
  2. 加密过程将 S-box 和明文进行 xor 运算,得到密文,解密过程也完全相同

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    /*加解密*/
    void rc4_crypt(unsigned char*s, unsigned char*Data, unsigned long Len)
    {
    int i = 0, j = 0, t = 0;
    unsigned long k = 0;
    unsigned char tmp;
    for (k = 0; k < Len; k++)
    {
    i = (i + 1) % 256;
    j = (j + s[i]) % 256;
    tmp = s[i];
    s[i] = s[j]; // 交换s[x]和s[y]
    s[j] = tmp;
    t = (s[i] + s[j]) % 256;
    Data[k] ^= s[t];
    }
    }

实现

下面是 C 实现的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include<stdio.h>
#include<string.h>
typedef unsigned longULONG;

/*初始化函数*/
void rc4_init(unsigned char*s, unsigned char*key, unsigned long Len)
{
int i = 0, j = 0;
char k[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i < 256; i++)
{
s[i] = i;
k[i] = key[i%Len];
}
for (i = 0; i < 256; i++)
{
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j]; // 交换s[i]和s[j]
s[j] = tmp;
}
}

/*加解密*/
void rc4_crypt(unsigned char*s, unsigned char*Data, unsigned long Len)
{
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k < Len; k++)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j]; // 交换s[x]和s[y]
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] ^= s[t];
}
}

int main()
{
unsigned char s[256] = { 0 }, s2[256] = { 0 }; // S-box
char key[256] = { "justfortest" };
char pData[512] = "这是一个用来加密的数据Data";
unsigned long len = strlen(pData);
int i;

printf("pData=%s\n", pData);
printf("key=%s,length=%d\n\n", key, strlen(key));
rc4_init(s, (unsigned char*)key, strlen(key)); // 已经完成了初始化
printf("完成对S[i]的初始化,如下:\n\n");
for (i = 0; i < 256; i++)
{
printf("%02X", s[i]);
if (i && (i + 1) % 16 == 0)putchar('\n');
}
printf("\n\n");
for (i = 0; i < 256; i++) // 用s2[i]暂时保留经过初始化的s[i],很重要的!!!
{
s2[i] = s[i];
}
printf("已经初始化,现在加密:\n\n");
rc4_crypt(s, (unsigned char*)pData, len); // 加密
printf("pData=%s\n\n", pData);
printf("已经加密,现在解密:\n\n");
rc4_crypt(s2, (unsigned char*)pData, len); // 解密
printf("pData=%s\n\n", pData);
return 0;
}

运行结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
C:\Users\thunder>"D:\AlgorithmTest.exe"
pData=这是一个用来加密的数据Data
key=justfortest,length=11

完成对S[i]的初始化,如下:

21E0944A8CAA5C851A95374358840E32
EE3AF7C8F67F898BFF52235F3B51CAE6
31E2A570C698C046CE836EB91EBC9235
FD6B1CB62C2D69B565631B933EA60762
13EAE7775BA159DD745491C181B7FB49
66037D2E47331538F8A820AE22D2345A
64FA3F87714DFCBF2490D32ADF9EB85E
0A2780E40CAD1497E3D8C7F2F4424176
DC8D45A9789DE1B0D9044F0F36C3C5BE
4C7AEB6C4B8640E59A7919B39BABAFE8
C4AC8EFE963CEDEF0B091202BAB1D001
CB60D4F91D557BCC7544D750F17E67C9
88DB111826F0B299B4BB482BA41FF58A
C2E9A0CF5DDA6FCD57003D0830A2A316
9F0D6AF36D682F8FBD28A7DE4ED15373
7C2956D51706058225EC617210399CD6


已经初始化,现在加密:

pData=?獤 5Ws?g&W鋟覈?T?

已经加密,现在解密:

pData=这是一个用来加密的数据Data


C:\Users\thunder>

上面的代码是rc4加密字符串这是一个用来加密的数据Data,key = justfortest,我们放入IDA观察,初始化函数如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void __cdecl rc4_init(char *s, char *key, unsigned int Len)
{
char tmp; // STDF_1
char k[256]; // [esp+DCh] [ebp-120h]
int j; // [esp+1E4h] [ebp-18h]
int i; // [esp+1F0h] [ebp-Ch]

j = 0;
k[0] = 0;
j__memset(&k[1], 0, 0xFFu);
for ( i = 0; i < 256; ++i )
{
s[i] = i;
k[i] = key[i % Len];
}
for ( i = 0; i < 256; ++i )
{
j = (k[i] + j + (unsigned __int8)s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
}

加密函数如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void __cdecl rc4_crypt(char *s, char *Data, unsigned int Len)
{
char tmp; // STD3_1
unsigned int k; // [esp+DCh] [ebp-2Ch]
int j; // [esp+F4h] [ebp-14h]
int i; // [esp+100h] [ebp-8h]

i = 0;
j = 0;
for ( k = 0; k < Len; ++k )
{
i = (i + 1) % 256;
j = (j + (unsigned __int8)s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
Data[k] ^= s[((unsigned __int8)s[j] + (unsigned __int8)s[i]) % 256];
}
}

辨别

从IDA中可以看到有很多的 %256 操作,因为 s 盒的长度为256,所以这里很好判断,如果在CTF逆向过程中看到有多次 %256 的操作最后又有异或的话那可以考虑是否是RC4密码

解密

python实现如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# -*- coding: utf-8 -*-
import random, base64
from hashlib import sha1

def crypt(data, key):
"""RC4 algorithm"""
x = 0
box = range(256)
for i in range(256):
x = (x + box[i] + ord(key[i % len(key)])) % 256
box[i], box[x] = box[x], box[i]
x = y = 0
out = []
for char in data:
x = (x + 1) % 256
y = (y + box[x]) % 256
box[x], box[y] = box[y], box[x]
out.append(chr(ord(char) ^ box[(box[x] + box[y]) % 256]))

return ''.join(out)


def tencode(data, key, encode=base64.b64encode, salt_length=16):
"""RC4 encryption with random salt and final encoding"""
salt = ''
for n in range(salt_length):
salt += chr(random.randrange(256))
data = salt + crypt(data, sha1(key + salt).digest())
if encode:
data = encode(data)
return data


def tdecode(data, key, decode=base64.b64decode, salt_length=16):
"""RC4 decryption of encoded data"""
if decode:
data = decode(data)
salt = data[:salt_length]
return crypt(data[salt_length:], sha1(key + salt).digest())


if __name__ == '__main__':
# 需要解密的数据
data = 'UUyFTj8PCzF6geFn6xgBOYSvVTrbpNU4OF9db9wMcPD1yDbaJw=='
# 密钥
key = 'welcometoicqedu'
# 解码
decoded_data = tdecode(data=data, key=key)
print("明文是:")
print decoded_data

输出如下

1
2
3
4
5
[Running] python -u "/home/thunder/Desktop/CTF/crypt/example/rc4_example/test.py"
明文是:
flag{rc4_l_keepgoing}

[Done] exited with code=0 in 0.14 seconds

在线解密网站:https://www.sojson.com/encrypt_rc4.html

0x03:SM4

介绍

SM4.0(原名SMS4.0)是中华人民共和国政府采用的一种分组密码标准,由国家密码管理局于2012年3月21日发布。相关标准为“GM/T 0002-2012《SM4分组密码算法》(原SMS4分组密码算法)”。在商用密码体系中,SM4主要用于数据加密,其算法公开,分组长度与密钥长度均为128bit,加密算法与密钥扩展算法都采用32轮非线性迭代结构,S盒为固定的8比特输入8比特输出。SM4.0中的指令长度被提升到大于64K(即64×1024)的水平,这是SM 3.0规格(渲染指令长度允许大于512)的128倍。

加密过程

这里我简要介绍一下SM4算法,详细的过程可以查看参考链接,首先我们要知道SM4是一个对称加密算法,也就是说加密和解密的密钥相同,首先我们要清楚下面几个概念

  • SM4是分组密码,所以我们要将明文分组,将明文分成128位一组

    1

  • S(Sbox)盒负责置换我们的明文

    2

  • 因为SM4面向的是32bit的字(word),S盒处理的是两个16进制数也就是8bit的字节,所以我们要用4个S盒来置换

    3

  • 轮函数F的概念如下图,以字为单位进行加密运算,称一次迭代运算为一轮变换

    4

  • 合成置换T就是非线性变换和线性变换的一个组合过程

    5

了解上述一些概念之后加密解密的过程如下图

6

在SM4算法中,轮秘钥的产生是通过用户选择主秘钥作为基本的秘钥数据,在通过一些算法生成轮秘钥,在密钥拓展中,我们通过一些常数对用户选择的主钥进行操作,增大随机性。密钥扩展算法如下

7

实现

代码出自这里

sm4.c加密解密函数的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
// sm4.c
// Test vector 1
// plain: 01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
// key: 01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
// round key and temp computing result:
// rk[ 0] = f12186f9 X[ 0] = 27fad345
// rk[ 1] = 41662b61 X[ 1] = a18b4cb2
// rk[ 2] = 5a6ab19a X[ 2] = 11c1e22a
// rk[ 3] = 7ba92077 X[ 3] = cc13e2ee
// rk[ 4] = 367360f4 X[ 4] = f87c5bd5
// rk[ 5] = 776a0c61 X[ 5] = 33220757
// rk[ 6] = b6bb89b3 X[ 6] = 77f4c297
// rk[ 7] = 24763151 X[ 7] = 7a96f2eb
// rk[ 8] = a520307c X[ 8] = 27dac07f
// rk[ 9] = b7584dbd X[ 9] = 42dd0f19
// rk[10] = c30753ed X[10] = b8a5da02
// rk[11] = 7ee55b57 X[11] = 907127fa
// rk[12] = 6988608c X[12] = 8b952b83
// rk[13] = 30d895b7 X[13] = d42b7c59
// rk[14] = 44ba14af X[14] = 2ffc5831
// rk[15] = 104495a1 X[15] = f69e6888
// rk[16] = d120b428 X[16] = af2432c4
// rk[17] = 73b55fa3 X[17] = ed1ec85e
// rk[18] = cc874966 X[18] = 55a3ba22
// rk[19] = 92244439 X[19] = 124b18aa
// rk[20] = e89e641f X[20] = 6ae7725f
// rk[21] = 98ca015a X[21] = f4cba1f9
// rk[22] = c7159060 X[22] = 1dcdfa10
// rk[23] = 99e1fd2e X[23] = 2ff60603
// rk[24] = b79bd80c X[24] = eff24fdc
// rk[25] = 1d2115b0 X[25] = 6fe46b75
// rk[26] = 0e228aeb X[26] = 893450ad
// rk[27] = f1780c81 X[27] = 7b938f4c
// rk[28] = 428d3654 X[28] = 536e4246
// rk[29] = 62293496 X[29] = 86b3e94f
// rk[30] = 01cf72e5 X[30] = d206965e
// rk[31] = 9124a012 X[31] = 681edf34
// cypher: 68 1e df 34 d2 06 96 5e 86 b3 e9 4f 53 6e 42 46
//
// test vector 2
// the same key and plain 1000000 times coumpting
// plain: 01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
// key: 01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
// cypher: 59 52 98 c7 c6 fd 27 1f 04 02 f8 04 c3 3d 3f 66

#include "sm4.h"
#include <string.h>
#include <stdio.h>

/*
* 32-bit integer manipulation macros (big endian)
*/
#ifndef GET_ULONG_BE
#define GET_ULONG_BE(n,b,i) \
{ \
(n) = ( (unsigned long) (b)[(i) ] << 24 ) \
| ( (unsigned long) (b)[(i) + 1] << 16 ) \
| ( (unsigned long) (b)[(i) + 2] << 8 ) \
| ( (unsigned long) (b)[(i) + 3] ); \
}
#endif

#ifndef PUT_ULONG_BE
#define PUT_ULONG_BE(n,b,i) \
{ \
(b)[(i) ] = (unsigned char) ( (n) >> 24 ); \
(b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \
(b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \
(b)[(i) + 3] = (unsigned char) ( (n) ); \
}
#endif

/*
*rotate shift left marco definition
*
*/
#define SHL(x,n) (((x) & 0xFFFFFFFF) << n)
#define ROTL(x,n) (SHL((x),n) | ((x) >> (32 - n)))

#define SWAP(a,b) { unsigned long t = a; a = b; b = t; t = 0; }

/*
* Expanded SM4 S-boxes
/* Sbox table: 8bits input convert to 8 bits output*/

static const unsigned char SboxTable[16][16] =
{
{ 0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05 },
{ 0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99 },
{ 0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62 },
{ 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6 },
{ 0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8 },
{ 0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35 },
{ 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87 },
{ 0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e },
{ 0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1 },
{ 0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3 },
{ 0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f },
{ 0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51 },
{ 0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8 },
{ 0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0 },
{ 0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84 },
{ 0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48 }
};

/* System parameter */
static const unsigned long FK[4] = { 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc };

/* fixed parameter */
static const unsigned long CK[32] =
{
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
};


/*
* private function:
* look up in SboxTable and get the related value.
* args: [in] inch: 0x00~0xFF (8 bits unsigned value).
*/
static unsigned char sm4Sbox(unsigned char inch)
{
unsigned char *pTable = (unsigned char *)SboxTable;
unsigned char retVal = (unsigned char)(pTable[inch]);
return retVal;
}

/*
* private F(Lt) function:
* "T algorithm" == "L algorithm" + "t algorithm".
* args: [in] a: a is a 32 bits unsigned value;
* return: c: c is calculated with line algorithm "L" and nonline algorithm "t"
*/
static unsigned long sm4Lt(unsigned long ka)
{
unsigned long bb = 0;
unsigned long c = 0;
unsigned char a[4];
unsigned char b[4];
PUT_ULONG_BE(ka, a, 0)
b[0] = sm4Sbox(a[0]);
b[1] = sm4Sbox(a[1]);
b[2] = sm4Sbox(a[2]);
b[3] = sm4Sbox(a[3]);
GET_ULONG_BE(bb, b, 0)
c = bb ^ (ROTL(bb, 2)) ^ (ROTL(bb, 10)) ^ (ROTL(bb, 18)) ^ (ROTL(bb, 24));
return c;
}

/*
* private F function:
* Calculating and getting encryption/decryption contents.
* args: [in] x0: original contents;
* args: [in] x1: original contents;
* args: [in] x2: original contents;
* args: [in] x3: original contents;
* args: [in] rk: encryption/decryption key;
* return the contents of encryption/decryption contents.
*/
static unsigned long sm4F(unsigned long x0, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long rk)
{
return (x0^sm4Lt(x1^x2^x3^rk));
}


/* private function:
* Calculating round encryption key.
* args: [in] a: a is a 32 bits unsigned value;
* return: sk[i]: i{0,1,2,3,...31}.
*/
static unsigned long sm4CalciRK(unsigned long ka)
{
unsigned long bb = 0;
unsigned long rk = 0;
unsigned char a[4];
unsigned char b[4];
PUT_ULONG_BE(ka, a, 0)
b[0] = sm4Sbox(a[0]);
b[1] = sm4Sbox(a[1]);
b[2] = sm4Sbox(a[2]);
b[3] = sm4Sbox(a[3]);
GET_ULONG_BE(bb, b, 0)
rk = bb ^ (ROTL(bb, 13)) ^ (ROTL(bb, 23));
return rk;
}

static void sm4_setkey(unsigned long SK[32], unsigned char key[16])
{
unsigned long MK[4];
unsigned long k[36];
unsigned long i = 0;

GET_ULONG_BE(MK[0], key, 0);
GET_ULONG_BE(MK[1], key, 4);
GET_ULONG_BE(MK[2], key, 8);
GET_ULONG_BE(MK[3], key, 12);
k[0] = MK[0] ^ FK[0];
k[1] = MK[1] ^ FK[1];
k[2] = MK[2] ^ FK[2];
k[3] = MK[3] ^ FK[3];
for (; i<32; i++)
{
k[i + 4] = k[i] ^ (sm4CalciRK(k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ CK[i]));
SK[i] = k[i + 4];
}

}

/*
* SM4 standard one round processing
*
*/
static void sm4_one_round(unsigned long sk[32],
unsigned char input[16],
unsigned char output[16])
{
unsigned long i = 0;
unsigned long ulbuf[36];

memset(ulbuf, 0, sizeof(ulbuf));
GET_ULONG_BE(ulbuf[0], input, 0)
GET_ULONG_BE(ulbuf[1], input, 4)
GET_ULONG_BE(ulbuf[2], input, 8)
GET_ULONG_BE(ulbuf[3], input, 12)
while (i<32)
{
ulbuf[i + 4] = sm4F(ulbuf[i], ulbuf[i + 1], ulbuf[i + 2], ulbuf[i + 3], sk[i]);
// #ifdef _DEBUG
// printf("rk(%02d) = 0x%08x, X(%02d) = 0x%08x \n",i,sk[i], i, ulbuf[i+4] );
// #endif
i++;
}
PUT_ULONG_BE(ulbuf[35], output, 0);
PUT_ULONG_BE(ulbuf[34], output, 4);
PUT_ULONG_BE(ulbuf[33], output, 8);
PUT_ULONG_BE(ulbuf[32], output, 12);
}

/*
* SM4 key schedule (128-bit, encryption)
*/
void sm4_setkey_enc(sm4_context *ctx, unsigned char key[16])
{
ctx->mode = SM4_ENCRYPT;
sm4_setkey(ctx->sk, key);
}

/*
* SM4 key schedule (128-bit, decryption)
*/
void sm4_setkey_dec(sm4_context *ctx, unsigned char key[16])
{
int i;
ctx->mode = SM4_ENCRYPT;
sm4_setkey(ctx->sk, key);
for (i = 0; i < 16; i++)
{
SWAP(ctx->sk[i], ctx->sk[31 - i]);
}
}


/*
* SM4-ECB block encryption/decryption
*/

void sm4_crypt_ecb(sm4_context *ctx,
int mode,
int length,
unsigned char *input,
unsigned char *output)
{
while (length > 0)
{
sm4_one_round(ctx->sk, input, output);
input += 16;
output += 16;
length -= 16;
}

}

/*
* SM4-CBC buffer encryption/decryption
*/
void sm4_crypt_cbc(sm4_context *ctx,
int mode,
int length,
unsigned char iv[16],
unsigned char *input,
unsigned char *output)
{
int i;
unsigned char temp[16];

if (mode == SM4_ENCRYPT)
{
while (length > 0)
{
for (i = 0; i < 16; i++)
output[i] = (unsigned char)(input[i] ^ iv[i]);

sm4_one_round(ctx->sk, output, output);
memcpy(iv, output, 16);

input += 16;
output += 16;
length -= 16;
}
}
else /* SM4_DECRYPT */
{
while (length > 0)
{
memcpy(temp, input, 16);
sm4_one_round(ctx->sk, input, output);

for (i = 0; i < 16; i++)
output[i] = (unsigned char)(output[i] ^ iv[i]);

memcpy(iv, temp, 16);

input += 16;
output += 16;
length -= 16;
}
}
}

sm4.h头文件,mode选择加密模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
* \file sm4.h
*/
#ifndef XYSSL_SM4_H
#define XYSSL_SM4_H

#define SM4_ENCRYPT 1
#define SM4_DECRYPT 0

/**
* \brief SM4 context structure
*/
typedef struct
{
int mode; /*!< encrypt/decrypt */
unsigned long sk[32]; /*!< SM4 subkeys */
}
sm4_context;


#ifdef __cplusplus
extern "C" {
#endif

/**
* \brief SM4 key schedule (128-bit, encryption)
*
* \param ctx SM4 context to be initialized
* \param key 16-byte secret key
*/
void sm4_setkey_enc(sm4_context *ctx, unsigned char key[16]);

/**
* \brief SM4 key schedule (128-bit, decryption)
*
* \param ctx SM4 context to be initialized
* \param key 16-byte secret key
*/
void sm4_setkey_dec(sm4_context *ctx, unsigned char key[16]);

/**
* \brief SM4-ECB block encryption/decryption
* \param ctx SM4 context
* \param mode SM4_ENCRYPT or SM4_DECRYPT
* \param length length of the input data
* \param input input block
* \param output output block
*/
void sm4_crypt_ecb(sm4_context *ctx,
int mode,
int length,
unsigned char *input,
unsigned char *output);

/**
* \brief SM4-CBC buffer encryption/decryption
* \param ctx SM4 context
* \param mode SM4_ENCRYPT or SM4_DECRYPT
* \param length length of the input data
* \param iv initialization vector (updated after use)
* \param input buffer holding the input data
* \param output buffer holding the output data
*/
void sm4_crypt_cbc(sm4_context *ctx,
int mode,
int length,
unsigned char iv[16],
unsigned char *input,
unsigned char *output);

#ifdef __cplusplus
}
#endif

#endif /* sm4.h */

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// test.c
#include <string.h>
#include <stdio.h>
#include "sm4.h"

int main()
{
unsigned char key[16] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 };
unsigned char input[16] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 };
unsigned char output[16];
sm4_context ctx;
unsigned long i;

//encrypt standard testing vector
sm4_setkey_enc(&ctx, key);
sm4_crypt_ecb(&ctx, 1, 16, input, output);
for (i = 0; i<16; i++)
printf("%02x ", output[i]);
printf("\n");

//解密测试
sm4_setkey_dec(&ctx, key);
sm4_crypt_ecb(&ctx, 0, 16, output, output);
for (i = 0; i<16; i++)
printf("%02x ", output[i]);
printf("\n");

//decrypt 1M times testing vector based on standards.
i = 0;
sm4_setkey_enc(&ctx, key);
while (i<1000000)
{
sm4_crypt_ecb(&ctx, 1, 16, input, input);
i++;
}
for (i = 0; i<16; i++)
printf("%02x ", input[i]);
printf("\n");

return 0;
}

运行结果

1
2
3
4
C:\Users\thunder>"D:\AlgorithmTest.exe"
68 1e df 34 d2 06 96 5e 86 b3 e9 4f 53 6e 42 46
01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10
59 52 98 c7 c6 fd 27 1f 04 02 f8 04 c3 3d 3f 66

解密

pysm4是国密SM4算法的Python实现,这里下载

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> from pysm4 import encrypt, decrypt
# 明文
>>> clear_num = 0x0123456789abcdeffedcba9876543210
# 密钥
>>> mk = 0x0123456789abcdeffedcba9876543210
# 加密
>>> cipher_num = encrypt(clear_num, mk)
>>> hex(cipher_num)[2:].replace('L', '')
'681edf34d206965e86b3e94f536e4246'
# 解密
>>> clear_num == decrypt(cipher_num, mk)
True
>>>

辨别

CTF逆向可以通过判断S盒的值来猜测SM4算法,通过S盒生成4个8位的字符,我们将上面实现代码放入IDA中查看,我们可以通过输入明文密钥的格式来猜测SM4算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
__int64 main()
{
int v0; // edx
__int64 v1; // ST0C_8
unsigned int i; // [esp+D0h] [ebp-E0h]
sm4_context ctx; // [esp+DCh] [ebp-D4h]
char output[16]; // [esp+168h] [ebp-48h]
char input[16]; // [esp+180h] [ebp-30h]
char key[16]; // [esp+198h] [ebp-18h]

key[0] = 1;
key[1] = 0x23;
key[2] = 0x45;
key[3] = 0x67;
key[4] = 0x89u;
key[5] = 0xABu;
key[6] = 0xCDu;
key[7] = 0xEFu;
key[8] = 0xFEu;
key[9] = 0xDCu;
key[10] = 0xBAu;
key[11] = 0x98u;
key[12] = 0x76;
key[13] = 0x54;
key[14] = 0x32;
key[15] = 0x10;
input[0] = 1;
input[1] = 0x23;
input[2] = 0x45;
input[3] = 0x67;
input[4] = 0x89u;
input[5] = 0xABu;
input[6] = 0xCDu;
input[7] = 0xEFu;
input[8] = 0xFEu;
input[9] = 0xDCu;
input[10] = 0xBAu;
input[11] = 0x98u;
input[12] = 0x76;
input[13] = 0x54;
input[14] = 0x32;
input[15] = 0x10;
j__sm4_setkey_enc(&ctx, key);
j__sm4_crypt_ecb(&ctx, 1, 16, input, output);
for ( i = 0; i < 0x10; ++i )
_printf("%02x ", (unsigned __int8)output[i]);
_printf("\n");
j__sm4_setkey_dec(&ctx, key);
j__sm4_crypt_ecb(&ctx, 0, 16, output, output);
for ( i = 0; i < 0x10; ++i )
_printf("%02x ", (unsigned __int8)output[i]);
_printf("\n");
i = 0;
j__sm4_setkey_enc(&ctx, key);
while ( i < 0xF4240 )
{
j__sm4_crypt_ecb(&ctx, 1, 16, input, input);
++i;
}
for ( i = 0; i < 0x10; ++i )
_printf("%02x ", (unsigned __int8)input[i]);
_printf("\n");
HIDWORD(v1) = v0;
LODWORD(v1) = 0;
return v1;
}

算法中的T变换观察返回值也有很明显的特征

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
unsigned int __cdecl sm4F(unsigned int x0, unsigned int x1, unsigned int x2, unsigned int x3, unsigned int rk)
{
return x0 ^ (unsigned __int64)sm4Lt(rk ^ x3 ^ x2 ^ x1); //返回多组异或
}

/************************************************************************************************/

__int64 __cdecl sm4Lt(unsigned int ka)
{
unsigned __int8 b; // STD8_1
unsigned __int8 b_1; // STD9_1
unsigned __int8 b_2; // STDA_1
unsigned __int8 v4; // al
unsigned int bb; // STFC_4
__int64 v6; // ST00_8

b = sm4Sbox(SHIBYTE(ka));
b_1 = sm4Sbox(SBYTE2(ka));
b_2 = sm4Sbox(SBYTE1(ka));
v4 = sm4Sbox(ka);
bb = v4 | (b_2 << 8) | (b_1 << 16) | (b << 24); // 分4组每组8位计算
HIDWORD(v6) = (bb >> 8) | (bb << 24);
LODWORD(v6) = HIDWORD(v6) ^ ((bb >> 14) | (bb << 18)) ^ ((bb >> 22) | (bb << 10)) ^ bb ^ ((bb >> 30) | 4 * bb);
return v6;
}

例题

2019ciscn-bbvvmm

下面的代码和上面的对比可以很容易的猜到SM4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
unsigned __int64 __fastcall sub_400EE2(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5)
{
return a1 ^ sub_400D87(a5 ^ a4 ^ a3 ^ a2);
}

/************************************************************************************************/

__int64 __cdecl sm4Lt(unsigned int ka)
{
unsigned __int8 b; // STD8_1
unsigned __int8 b_1; // STD9_1
unsigned __int8 b_2; // STDA_1
unsigned __int8 v4; // al
unsigned int bb; // STFC_4
__int64 v6; // ST00_8

b = sm4Sbox(SHIBYTE(ka));
b_1 = sm4Sbox(SBYTE2(ka));
b_2 = sm4Sbox(SBYTE1(ka));
v4 = sm4Sbox(ka);
bb = v4 | (b_2 << 8) | (b_1 << 16) | (b << 24);
HIDWORD(v6) = (bb >> 8) | (bb << 24);
LODWORD(v6) = HIDWORD(v6) ^ ((bb >> 14) | (bb << 18)) ^ ((bb >> 22) | (bb << 10)) ^ bb ^ ((bb >> 30) | 4 * bb);
return v6;
}

0x04:Reference

Base

1
2
http://www.cnblogs.com/hongru/archive/2012/01/14/2321397.html
https://blog.csdn.net/u011491972/article/details/52800177

RC4

1
2
https://blog.csdn.net/Fly_hps/article/details/79918495
https://baike.baidu.com/item/RC4/3454548?fr=aladdin

SM4

1
2
3
4
https://neuqzxy.github.io/2017/06/15/%E6%AC%A3%E4%BB%94%E5%B8%A6%E4%BD%A0%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8SM4%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95/
https://baike.baidu.com/item/SM4.0/3901780?fr=aladdin
https://max.book118.com/html/2018/1023/8017013004001130.shtm
https://blog.csdn.net/cg129054036/article/details/83012721